package logic

import (
	"cloud-entropy-storage-system/common/pkg/pathdir"
	"cloud-entropy-storage-system/volume/internal/svc"
	"cloud-entropy-storage-system/volume/internal/types"
	"encoding/binary"
	"errors"
	"github.com/asdine/storm/v3"
	"go.uber.org/zap"
	"os"
	"path"

	"strconv"
	"sync"
	"time"
)

type Volume struct {
	Id        uint64
	WriteAble bool
	index     *types.StormIndex
	status    *types.Status
	dataFile  *os.File
	mutex     sync.Mutex
	log       *zap.Logger
}

func NewVolume(dir string, vid uint64, db *storm.DB, log *zap.Logger) (v *Volume, err error) {
	_, _ = pathdir.CheckAndMkDir(dir)
	filePath := path.Join(dir, strconv.FormatUint(vid, 10)+".data")
	v = new(Volume)
	v.Id = vid
	v.dataFile, err = os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		if os.IsPermission(err) {
			v.dataFile, err = os.OpenFile(filePath, os.O_RDONLY, 0)
			if err != nil {
				return nil, err
			}
			v.WriteAble = false
		} else {
			return nil, err
		}
	}
	v.WriteAble = true
	v.index = types.NewIndex(db)
	v.status = types.NewStatus(vid, db)
	v.log = log
	return v, nil
}

func (v *Volume) Delete(fid uint64) error {
	v.mutex.Lock()
	defer v.mutex.Unlock()
	fi, err := v.index.Get(fid)
	if err != nil {
		v.log.Error(err.Error())
		return err
	}
	if fi != nil && fi.Flag == svc.FlagDelete {
		return errors.New("文件已删除")
	}
	return v.index.Delete(fi.ID)
}

func (v *Volume) Get(fid uint64) (*svc.File, error) {
	fi, err := v.index.Get(fid)
	if err == nil {
		if fi.Flag == svc.FlagDelete {
			return nil, errors.New("文件不存在")
		}
		return &svc.File{DataFile: v.dataFile, Info: fi}, nil
	} else {
		return nil, err
	}
}

func (v *Volume) NewFile(fid, size uint64, fileName, md string) (*svc.File, error) {
	v.mutex.Lock()
	defer v.mutex.Unlock()
	if v.index.Has(fid) {
		return nil, errors.New("fid已存在")
	}

	offset := v.getOffset()
	//在文件内容前后都写入fid
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, fid)
	_, err := v.dataFile.WriteAt(b, int64(offset))
	if err != nil {
		return nil, err
	}
	_, err = v.dataFile.WriteAt(b, int64(offset+8+size))
	if err != nil {
		return nil, err
	}

	fileInfo := &svc.FileInfo{
		Fid:      fid,
		Offset:   offset + 8,
		Size:     size,
		Ctime:    time.Now(),
		Mtime:    time.Now(),
		Flag:     svc.FlagInit,
		FileName: fileName,
		MD:       md,
	}
	err = v.index.Set(fileInfo)
	if err != nil {
		return nil, err
	} else {
		_ = v.status.SetOffset(offset + size + 16)
		return &svc.File{DataFile: v.dataFile, Info: fileInfo}, nil
	}
}

func (v *Volume) GetSize() int64 {
	stat, err := v.dataFile.Stat()
	if err != nil {
		return 0
	}
	return stat.Size()
}

func (v *Volume) getOffset() uint64 {
	s, _ := v.status.GetOffset()
	return s
}

func (v *Volume) Close() {
	// 因为要退出,所以不解锁,禁止读写
	v.mutex.Lock()

	//将所有资源释放
	v.dataFile.Close()
	v.dataFile = nil

	v.status.Close()
	v.status = nil

	v.index.Close()
	v.index = nil
}
